package com.web.main.utils;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static java.lang.Math.PI;

/**
 * 经纬度计算类
 * @create 2020-11-26 22:04
 */

public class DistanceUtil {

    private static double  DEF_PI = PI;// 3.14159265359
    private static double  DEF_2PI =2 * PI;// 6.28318530712
    private static double  DEF_PI180 = PI/ 180.0;// 0.01745329252
    private static double  DEF_R =6370996.81;// 地球的半径

    /**
     * 通过两点的经纬度计算两点直线距离
     * @param lng1Str  终端经度
     * @param lat1Str  终端纬度
     * @param lng2Str  商家经度
     * @param lat2Str  商家纬度
     * @return 返回的值四舍五入并保留两位小数,单位米
     */
    public static Double getTwopointsDistance(String lng1Str,String lat1Str, String lng2Str,String lat2Str) {
        if ((StringUtils.isBlank(lat1Str)) || (StringUtils.isBlank(lng1Str))
                || (StringUtils.isBlank(lat2Str))
                || (StringUtils.isBlank(lng2Str))){return null;}

        Double lon1 = Double.parseDouble(lng1Str.trim());
        Double lat1 = Double.parseDouble(lat1Str.trim());
        Double lon2 = Double.parseDouble(lng2Str.trim());
        Double lat2 = Double.parseDouble(lat2Str.trim());

        // 角度转换为弧度
        double ew1 =  lon1 * DEF_PI180;
        double ns1 =  lat1 * DEF_PI180;
        double ew2 =  lon2 * DEF_PI180;
        double ns2 =  lat2 * DEF_PI180;

        // 求大圆劣弧与球心所夹的角(弧度)
        double distance = Math.sin(ns1) * Math.sin(ns2)+ Math.cos(ns1)* Math.cos(ns2)* Math.cos(ew1- ew2);
        // 调整到[-1..1]范围内，避免溢出
        if(distance >1.0) distance = 1.0;
        else if(distance <-1.0) distance = -1.0;
        distance = DEF_R *Math.acos(distance);

        //格式化返回值
        BigDecimal bd = new BigDecimal(distance);
        return distance = bd.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 判断两点距离是否在比较距离之内
     * @param distance 需要比较的距离
     * @param lng1Str  终端经度
     * @param lat1Str  终端经度
     * @param lng2Str  商家经度
     * @param lat2Str  商家纬度
     * @return
     */
    public static boolean isWithin(Integer distance,String lng1Str,String lat1Str, String lng2Str,String lat2Str){
        if (distance == null || distance <= 0) {
            return false;
        }
        if ((StringUtils.isBlank(lat1Str)) || (StringUtils.isBlank(lng1Str)) || (StringUtils.isBlank(lat2Str)) || (StringUtils.isBlank(lng2Str))){
            return false;
        }
        //根据两个点算距离
        Double calDistance =  getTwopointsDistance(lng1Str,lat1Str,lng2Str,lat2Str);

        if(calDistance != null){
            //判断指定距离是否大于真实距离
            return distance >= (calDistance.intValue());
        }
        return false;
    }

    /**
     * 根据距离排序实体
     * @param map key:比较的距离,value:距离对应的实体
     * @return 排序过后额实体
     */
    public static <T> List<T> sortByDistance(Map<Double, T> map){
        if(!CollectionUtils.isEmpty(map)){
            List<Double> distanceList = new ArrayList<Double>();
            for(Double distance : map.keySet()){
                distanceList.add(distance);
            }
            //System.out.println("排序前："+distanceList.toString());
            Collections.sort(distanceList);//升序排序
            //System.out.println("排序后："+distanceList.toString());

            List<T> returnList = new ArrayList<T>();
            for(Double d : distanceList){
                returnList.add(map.get(d));
            }
            return returnList;
        }
        return null;
    }

    /**
     * 计算地球上任意两点(经纬度)距离
     *
     * @param long1 第一点经度
     * @param lat1  第一点纬度
     * @param long2 第二点经度
     * @param lat2  第二点纬度
     * @return 返回距离 单位：米
     */
    public static double distanceByLongNLat(double long1, double lat1, double long2, double lat2) {
        double a, b, R;
        R = 6378137;//地球半径
        lat1 = lat1 * PI / 180.0;
        lat2 = lat2 * PI / 180.0;
        a = lat1 - lat2;
        b = (long1 - long2) * PI / 180.0;
        double d;
        double sa2, sb2;
        sa2 = Math.sin(a / 2.0);
        sb2 = Math.sin(b / 2.0);
        d = 2 * R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2));
        return d;
    }

    /**
     * 根据经纬度和半径计算经纬度范围
     * @param longitude 经度
     * @param latitude 纬度
     * @param raidus 单位米
     * @return  minLng-最小经度, minLat-最小纬度, maxLng, maxLat
     */
    public static Double[] getAround(double longitude, double latitude,  int raidus) {

        Double degree = (24901 * 1609) / 360.0;
        double raidusMile = raidus;

        Double dpmLat = 1 / degree;
        Double radiusLat = dpmLat * raidusMile;
        Double minLat = latitude - radiusLat;
        Double maxLat = latitude + radiusLat;

        Double mpdLng = degree * Math.cos(latitude * (PI / 180));
        Double dpmLng = 1 / mpdLng;
        Double radiusLng = dpmLng * raidusMile;
        Double minLng = longitude - radiusLng;
        Double maxLng = longitude + radiusLng;
        return new Double[]{minLng, minLat, maxLng, maxLat};
    }

}
